#region MIT Licence
/*
 * Copyright (C) 2014 PdfCreative
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
 * and associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom 
 * the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included 
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 
 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#endregion

using System;
using System.Collections.Generic;
using System.Text;

namespace PdfCreative.Pdf.Writer
{
    /// <summary>
    /// The PdfDictionary class represents a dictionary in a PDF object as it appears in a PDF document.
    /// It has a number of entries, each with a key and a value.
    /// </summary>
    public class PdfDictionary : PdfValue
    {
        // Private fields
        // ==============
        #region Private fields
        private Dictionary<string, PdfValue> _dictionary;
        #endregion



        // Base class overrides
        // ====================
        #region PdfValue overrides
        /// <summary>
        /// Writes the list to the specified PDF value writer.
        /// </summary>
        /// <param name="writer">The writer</param>
        internal override void Write(IPdfValueWriter writer)
        {
            writer.WriteDictionary(this);
        }
        #endregion



        // Public properties
        // =================
        #region Public properties
        /// <summary>
        /// Retrieves the number of items in the dictionary.
        /// </summary>
        public int Count
        {
            get { return _dictionary.Count; }
        }

        /// <summary>
        /// Retrieves the string value of the value with the specified key.
        /// If no item with that key exists, this method returns a Null reference.
        /// </summary>
        /// <param name="key">The key</param>
        /// <returns>The value, or a Null reference if no value with that key exists.</returns>
        public string this[string key]
        {
            get
            {
                if (null == key) throw new ArgumentNullException("key");

                String result = null;
                PdfValue value = null;
                if (_dictionary.TryGetValue(key, out value))
                    result = value.ToString();

                return result;
            }
            set
            {
                _dictionary[key] = new PdfPrimitive(value);
            }
        }
        #endregion



        // Public methods
        // ==============
        #region Public methods
        /// <summary>
        /// Adds a new entry with the specified key and value to the dictionary.
        /// If the key already exists, an exception is thrown.
        /// </summary>
        /// <param name="key">The key</param>
        /// <param name="value">The value</param>
        public void Add(string key, PdfValue value)
        {
            _dictionary.Add(key, value);
        }

        /// <summary>
        /// Adds a new entry with the specified key and string value to the dictionary.
        /// The string will be wrapped in a PdfPrimitive instance.
        /// If the key already exists, an exception is thrown.
        /// </summary>
        /// <param name="key">The key</param>
        /// <param name="value">The string value</param>
        public void Add(string key, string value)
        {
            _dictionary.Add(key, new PdfPrimitive(value));
        }

        /// <summary>
        /// Retrieves the Boolean value with the specified key. If no entry with that key exists, or
        /// the entry is not a Boolean, this method returns the specified default value.
        /// </summary>
        /// <param name="key">The key</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns>The Boolean, or the default value.</returns>
        public bool GetBooleanValue(string key, bool defaultValue)
        {
            bool result = defaultValue;

            PdfValue value = GetValue(key);
            if (null != value)
                if (false == Boolean.TryParse(value.ToString(), out result))
                    result = defaultValue;

            return result;
        } // GetBooleanValue()

        /// <summary>
        /// Retrieves the decimal value with the specified key. If no entry with that key exists, or
        /// the entry is not a decimal, this method returns the specified default value.
        /// </summary>
        /// <param name="key">The key</param>
        /// <returns>The decimal, or the default value.</returns>
        public decimal GetDecimalValue(string key)
        {
            return GetDecimalValue(key, 0m);
        }

        /// <summary>
        /// Retrieves the decimal value with the specified key. If no entry with that key exists, or
        /// the entry is not a decimal, this method returns the specified default value.
        /// </summary>
        /// <param name="key">The key</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns>The decimal, or the default value.</returns>
        public decimal GetDecimalValue(string key, decimal defaultValue)
        {
            decimal result = defaultValue;

            PdfValue value = GetValue(key);
            if (null != value)
                if (false == Decimal.TryParse(value.ToString(), out result))
                    result = defaultValue;

            return result;
        } // GetDecimalValue()

        /// <summary>
        /// Retrieves the dictionary value with the specified key. If no entry with that key exists, or
        /// the entry is not a dictionary, this method returns a Null reference.
        /// </summary>
        /// <param name="key">The key</param>
        /// <returns>The list, or a Null reference if no dictionary was found</returns>
        public PdfDictionary GetDictionary(string key)
        {
            return GetValue(key) as PdfDictionary;
        }

        /// <summary>
        /// Retrieves the entries in this dictionary.
        /// </summary>
        /// <returns>The entries</returns>
        public IEnumerable<KeyValuePair<string, PdfValue>> GetEntries()
        {
            foreach(KeyValuePair<string, PdfValue> entry in _dictionary)
                yield return entry;
        }

        /// <summary>
        /// Retrieves the integer value with the specified key. If no entry with that key exists, or
        /// the entry is not an integer, this method returns the specified default value.
        /// </summary>
        /// <param name="key">The key</param>
        /// <returns>The integer, or the default value.</returns>
        public int GetInt32Value(string key)
        {
            return GetInt32Value(key, 0);
        }

        /// <summary>
        /// Retrieves the integer value with the specified key. If no entry with that key exists, or
        /// the entry is not an integer, this method returns the specified default value.
        /// </summary>
        /// <param name="key">The key</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns>The integer, or the default value.</returns>
        public int GetInt32Value(string key, int defaultValue)
        {
            int result = defaultValue;

            PdfValue value = GetValue(key);
            if (null != value)
                if (false == Int32.TryParse(value.ToString(), out result))
                    result = defaultValue;

            return result;
        } // GetInt32Value()

        /// <summary>
        /// Retrieves the list value with the specified key. If no entry with that key exists, or
        /// the entry is not a list, this method returns a Null reference.
        /// </summary>
        /// <param name="key">The key</param>
        /// <returns>The list, or a Null reference if no list was found</returns>
        public PdfList GetList(string key)
        {
            return GetValue(key) as PdfList;
        }

        /// <summary>
        /// Retrieves the long integer value with the specified key. If no entry with that key exists, or
        /// the entry is not a long integer, this method returns the specified default value.
        /// </summary>
        /// <param name="key">The key</param>
        /// <returns>The long integer, or the default value.</returns>
        public long GetLongValue(string key)
        {
            return GetLongValue(key, 0L);
        }

        /// <summary>
        /// Retrieves the long integer value with the specified key. If no entry with that key exists, or
        /// the entry is not a long integer, this method returns the specified default value.
        /// </summary>
        /// <param name="key">The key</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns>The long integer, or the default value.</returns>
        public long GetLongValue(string key, long defaultValue)
        {
            long result = defaultValue;

            PdfValue value = GetValue(key);
            if (null != value)
                if (false == Int64.TryParse(value.ToString(), out result))
                    result = defaultValue;

            return result;
        } // GetlongValue()

        /// <summary>
        /// Retrieves the object reference value with the specified key. If no entry with that key exists, or
        /// the entry is not an object reference, this method returns a Null reference.
        /// </summary>
        /// <param name="key">The key</param>
        /// <returns>The object reference, or a Null reference if no dictionary was found</returns>
        public PdfObjectReference GetObjectReference(string key)
        {
            return GetValue(key) as PdfObjectReference;
        }

        /// <summary>
        /// Retrieves the value with the specified key. If no value with that key
        /// exists, this method returns a Null reference.
        /// </summary>
        /// <param name="key">The key</param>
        /// <returns>The value with that key, or a Null reference if no value with that key exists.</returns>
        public PdfValue GetValue(string key)
        {
            if (null == key) throw new ArgumentNullException("key");

            PdfValue value = null;
            _dictionary.TryGetValue(key, out value);
            
            return value;
        }

        /// <summary>
        /// Retrieves the value with the specified key, or the default if no value with that key exists.
        /// </summary>
        /// <typeparam name="T">The type of the value.</typeparam>
        /// <param name="key">The key</param>
        /// <returns>The item with that key, or the default if no item with that key exists.</returns>
        public T GetValue<T>(string key) where T:PdfValue
        {
            T result = default(T);

            PdfValue value = GetValue(key);
            if (null != value)
                result = (T)value;

            return result;            
        }

        /// <summary>
        /// Removes the entry with the specified key.
        /// </summary>
        /// <param name="key">The key</param>
        public void Remove(string key)
        {
            if (null == key) throw new ArgumentNullException("key");
            _dictionary.Remove(key);
        }

        /// <summary>
        /// Adds a new entry with the specified key and value to the dictionary.
        /// </summary>
        /// <param name="key">The key</param>
        /// <param name="value">The value</param>
        public void Set(string key, PdfValue value)
        {
            _dictionary[key] = value;
        }

        #endregion



        // Public constructor
        // ==================
        #region Public constructor
        /// <summary>
        /// Constructs a new PdfDictionary instance.
        /// </summary>
        public PdfDictionary()
        {
            _dictionary = new Dictionary<string, PdfValue>();
        }
        #endregion

    } // PdfDictionary class
}
